import html
import os
import re

import markdown
from bs4 import BeautifulSoup
from loguru import logger
from markdown.extensions import codehilite
from pyquery import PyQuery

from ..markdown_document import MarkdownDocument


class WxHtmler:
    def __init__(self, html_template_dir: str):
        self.template_dir = str(html_template_dir)
        logger.debug(f"HTML_TEMPLATE_DIR: {self.template_dir}")
        # 设置类常量
        self.HIGHLIGHT_COLOR = "#7b0c00"  # 高亮颜色（默认为暗红色）
        self.WXCARD_ID = "@wxcard@"  # 微信公众号卡片的占位符
        self.WXCARD_TMPL = "wxcard"  # 微信公众号卡片的style
        self.PARA_TMPL = "para"  # 段落的style
        self.HEADER_TMPL = "header"  # 文章容器包裹的style
        self.LINK_TMPL = "link"  # 文中链接的style
        self.REF_LINK_TMPL = "ref_link"  # 参考链接的style
        self.FIGURE_TMPL = "figure"  # 图片的style
        self.REF_HEADER_TMPL = "ref_header"  # 参考文献的style
        self.SUB_NUM_TMPL = "sub_num"  # H2 标题 的Style，有数字序号
        self.SUB_TMPL = "sub"  # 其他 H 标题的style

    def _to_html(self, md_doc: MarkdownDocument) -> str:
        # 1. replace image urls with wechat urls
        content = md_doc.body.to_string()
        for image, meta in md_doc.uploaded_images.items():
            orig = "({})".format(image)
            new = "({})".format(meta[1])
            content = content.replace(orig, new)
        # 2. Convert Markdown to HTML
        html = self._md_to_original_html(content)
        # 3. beautify html with special css
        html = self.__css_beautify(html)
        return html

    def generate_article(self, md_doc: MarkdownDocument) -> dict:
        # Convert document to HTML
        html_content = self._to_html(md_doc)

        for key in md_doc.uploaded_images.keys():
            logger.debug(
                f"uploaded image {key} =>{md_doc.uploaded_images[key][0]} and {md_doc.uploaded_images[key][1]}"
            )
        # Extract metadata from front matter
        title = md_doc.front_matter.get("title", "Untitled")
        author = md_doc.front_matter.get("author", "乔梁")
        subtitle = md_doc.front_matter.get("description", "")
        source_url = md_doc.front_matter.get("SOURCE_URL", "")
        categories = md_doc.front_matter.get("categories", "")
        thumb_media_id = md_doc.uploaded_images[
            md_doc.front_matter.banner_imgRef.url_in_text
        ][0]
        # Assemble article data
        article = {
            "title": title,
            "author": author,
            "digest": subtitle,
            "show_cover_pic": 1,  # Display cover picture
            "content": html_content,
            "content_source_url": source_url,
            "need_open_comment": 1,
            "thumb_media_id": thumb_media_id,
            # 原创声明配置
            "content_security_declaration": {
                "content_security_type": 0,
                "need_open_original": True,
                "user_declare_type": 0,
                "original_platform_list": [],
            },
            # 合集ID（可选）
            "article_subject_id": categories,
        }

        return article

    def _md_to_original_html(self, content: str) -> str:
        """渲染 Markdown 内容为 HTML"""
        exts = [
            "markdown.extensions.extra",
            "markdown.extensions.tables",
            "markdown.extensions.toc",
            "markdown.extensions.sane_lists",
            codehilite.makeExtension(
                guess_lang=False, noclasses=False, pygments_style="monokai"
            ),
        ]
        html_content = markdown.markdown(content, extensions=exts)
        return html_content

    def _fix_triple_backticks_to_html(self, article: str) -> str:
        """将三个反引号的代码块转换为 微信公众号格式的代码块 .
        格式要示：代码块在pre元素有样式，而且每一行都需要用 <code> 标签包裹，
        """
        if not article or article.strip() == "":
            return article
        # 使用 BeautifulSoup 解析 HTML
        soup = BeautifulSoup(article, "html.parser")
        # 查找所有<pre>标签
        pre_tags = soup.find_all("pre")

        for pre_tag in pre_tags:
            # 创建一个<section>标签，包裹原有的<pre>标签内容
            section_tag = soup.new_tag("section")
            section_tag["class"] = ["code-snippet__js"]

            # 获取<pre>标签中的<code>标签
            code_tag = pre_tag.find("code")

            if code_tag:
                # 复制原始的 pre_tag
                cloned_pre_tag = BeautifulSoup(str(pre_tag), "html.parser").find("pre")
                # 清空克隆的 pre_tag 中的内容，因为我们将逐行添加
                cloned_pre_tag.clear()

                # 拆分每一行，处理为单独的<code>标签
                lines = code_tag.get_text().split("\n")
                # 将每行包裹在新的<code>标签内
                for line in lines:
                    if line.strip() == "":
                        continue
                    new_code_tag = soup.new_tag("code")
                    new_code_tag.string = line
                    cloned_pre_tag.append(new_code_tag)
                # 为 cloned_pre_tag 添加样式
                cloned_pre_tag["class"] = [
                    "code-snippet__js",
                    "code-snippet",
                    "code-snippet_nowrap",
                ]
                # 将处理后的 pre_tag 添加到 section_tag 中
                section_tag.append(cloned_pre_tag)

                # 将 <pre>标签用 新的<section>标签包裹
                pre_tag.replace_with(section_tag)

        # 返回修改后的 HTML 字符串
        return str(soup)

    def __css_beautify(self, content: str) -> str:
        """美化 HTML 内容"""
        content = self._replace_para(content)
        content = self._replace_header(content)
        content = self._replace_links(content)
        content = self._format_fix(content)
        content = self._fix_image(content)
        content = self._replace_wxcard(content)  # 替换微信公众号的名片
        # 下面必须在最后执行，它为整个内容加入头尾
        content = self._gen_css(self.HEADER_TMPL) + content + "</section>"

        return content

    def _replace_para(self, content: str) -> str:
        """替换段落标签样式"""
        res = []
        for line in content.split("\n"):
            if line.startswith("<p>"):
                line = line.replace("<p>", self._gen_css(self.PARA_TMPL))
            res.append(line)
        return "\n".join(res)

    def _replace_wxcard(self, content: str) -> str:
        newcontent = self._gen_css(self.WXCARD_TMPL)
        return content.replace(self.WXCARD_ID, newcontent)

    def _gen_css(self, path: str, *args) -> str:
        """生成 CSS 样式"""
        template_path = os.path.join(self.template_dir, f"{path}.tmpl")
        with open(template_path, "r", encoding="utf-8") as f:
            tmpl = f.read()
        return tmpl.format(*args)

    def _replace_header(self, content: str) -> str:
        """替换标题标签样式
        这个方法处理 HTML 中的标题标签（h1-h6），为其添加自定义样式。
        对于 h2 标签，使用特殊的带编号样式（sub_num.tmpl）
        对于其他标签，使用普通样式（sub.tmpl）
        """
        res = []
        lines = content.split("\n")
        h2_counter = 1  # 跟踪 h2 的序号
        i = 0
        while i < len(lines):
            line = lines[i].strip()

            if line.startswith("<h2") and line.endswith(">"):
                # 收集完整的 h2 标签内容
                h2_content = []
                while i < len(lines) and "</h2>" not in lines[i]:
                    h2_content.append(lines[i])
                    i += 1
                if i < len(lines):  # 添加结束标签
                    h2_content.append(lines[i])

                # 从内容中提取文本
                h2_text = "".join(h2_content)
                if ">" in h2_text and "<" in h2_text:
                    value = h2_text.split(">")[1].split("<")[0]
                else:
                    value = ""

                # 生成新的 h2 样式
                res.append(
                    self._gen_css(
                        "sub_num",
                        "h2",
                        self.HIGHLIGHT_COLOR,
                        str(h2_counter),
                        "17",
                        value,
                        "h2",
                    )
                )
                h2_counter += 1
                i += 1
                continue

            elif line.startswith("<h") and line.endswith(">"):
                # 处理其他标题标签
                tag = line.split(" ")[0].replace("<", "")
                value = line.split(">")[1].split("<")[0]
                digit = tag[1]
                font = (
                    (13 + (4 - int(tag[1])) * 2)
                    if (digit >= "0" and digit <= "9")
                    else 13
                )
                res.append(
                    self._gen_css("sub", tag, font, self.HIGHLIGHT_COLOR, value, tag)
                )
            else:
                res.append(lines[i])
            i += 1

        return "\n".join(res)

    def _replace_links(self, content: str) -> str:
        """替换链接样式"""
        pq = PyQuery(content)

        links = pq("a")
        refs = []
        index = 1

        if len(links) == 0:
            return content

        for item in links.items():
            link = self._gen_css(self.LINK_TMPL, item.attr("href"), item.text())
            index += 1
            refs.append([item.attr("href"), item.text(), link])

        for r in refs:
            orig = f'<a href="{html.escape(r[0])}">{r[1]}</a>'
            content = content.replace(orig, r[2])

        content = content + "\n" + self._gen_css(self.REF_HEADER_TMPL)
        content = content + """<section class="footnotes">"""

        for index, r in enumerate(refs, 1):
            line = self._gen_css(self.REF_LINK_TMPL, index, r[1], r[0])
            content += line + "\n"

        content = content + "</section>"
        return content

    def _fix_image(self, content: str) -> str:
        pq = PyQuery(content)
        imgs = pq("img")
        for line in imgs.items():
            link = """<img alt="{}" src="{}" />""".format(
                line.attr("alt"), line.attr("src")
            )
            figure = self._gen_css(self.FIGURE_TMPL, link, line.attr("alt"))
            content = content.replace(link, figure)
        return content

    def _fix_list(self, content: str) -> str:
        """修复列表格式问题"""
        # 将每个列表项之间的换行符替换为空格
        content = re.sub(r"</li>[\n\s]*<li>", "</li><li>", content, flags=re.DOTALL)
        content = re.sub(r"<ul>[\n\s]*<li>", "<ul><li>", content, flags=re.DOTALL)
        content = re.sub(r"</li>[\n\s]*</ul>", "</li></ul>", content, flags=re.DOTALL)
        content = re.sub(r"<ol>[\n\s]*<li>", "<ol><li>", content, flags=re.DOTALL)
        content = re.sub(r"</li>[\n\s]*</ol>", "</li></ol>", content, flags=re.DOTALL)

        return content

    def _format_fix(self, content: str) -> str:
        """修复其他格式问题"""
        # 由于有两类 triple backtick 和 single backtick code blocks,
        # 所以需要分别处理，并且处理顺序很重要，不可交换位置
        content = self._fix_triple_backticks_to_html(content)
        content = self._fix_single_backtick_to_html(content)

        # 修改 <ul>和<ol>，使其符合微信公众号的格式,即列表项之间没有任何换行
        content = self._fix_list(content)
        # content = content.replace("<ul>", "<ul class=\"list\">")

        return content

    def _fix_single_backtick_to_html(self, content: str) -> str:
        """修复 single_backtick "`"" code blocks.
        找到所有不被 <pre> 标签包含的 <code> 标签，并为这些标签添加 style="color: red;" 属性。
        """
        soup = BeautifulSoup(content, "html.parser")
        # 找到所有 <code> 标签
        code_tags = soup.find_all("code")
        for code_tag in code_tags:
            # 检查父标签是否是 <pre>
            if code_tag.find_parent("pre") is None:
                # 如果不是 <pre> 的子标签，则添加 style 属性
                if "style" in code_tag.attrs:
                    code_tag["style"] += "; color: #7b0c00;"
                else:
                    code_tag["style"] = "color: #7b0c00;"
        return str(soup)
